<template>
  <div class="tp-video" ref="tpVideoRef" :style="[style]" @mousemove="mouseover" @mouseleave="mouseleave">
    <canvas class="video-canvas" ref="videoCanvas" :width="width" :height="height"></canvas>
    <video ref=""></video>
    <div class="tp-video-box" v-if="hiddeiAction">
      <div class="tp-progres-box" ref="progresBoxRef" @mousedown.stop.prevent="handleClickProgres">
        <!-- 播放进度 -->
        <div class="tp-progres" :style="{ width: progres + '%' }">
          <div class="tp-progres-mark"></div>
        </div>
        <div class="buffered" :style="{ width: videoBuffered + '%' }"></div>
      </div>
      <div class="tp-progres-time">{{ currentTime + '/' + duration }}</div>
      <div class="i-mdi:fullscreen fullscreen-icon" @click="toggleFullScreen"></div>
    </div>
    <slot v-if="slots.videoBtn && hiddeiAction" name="videoBtn"></slot>
    <div class="tp-video-btn" @click="handleClick" v-if="hiddeiAction">
      <div class="icon-play" :class="[isPlay ? 'i-mdi:pause' : 'i-mdi:play']"></div>
    </div>
  </div>
</template>
<script setup lang="ts" name="TpVideo">
import { ref, shallowRef, onUnmounted, useSlots, onMounted } from 'vue'
// import { dayjs } from 'element-plus'
import { formatSeconds } from '@/utils'
const slots = useSlots()
const emit = defineEmits(['on-change', 'end'])
const props = withDefaults(defineProps<{ src: string, poster?: string, width?: string | number, height?: string | number }>(), {
  src: '',
  poster: '',
  width: 0,
  height: 0,
})
const progresBoxRef = ref() // 进度根节点
const tpVideoRef = ref() // 视频容器根节点
const videoCanvas = shallowRef() // canvas节点
const videoCanvasCtx = shallowRef() // canvas上下文
const timer = ref<any>() // 视频渲染定时器
const hiddeiAction = ref(true) // 是否显示控制器
const isFullScreen = ref(false) // 是否全屏
const videoBuffered = ref(0) // 缓冲进度

const videoRef = ref() // video节点
const progres = ref<number>(0) // 当前播放进度
const isPlay = ref<boolean>(false) // 当前播放状态
const duration = ref('00:00:00') // 视频总时长
const currentTime = ref('00:00:00') // 当前播放视频时长
const videoId = ref() // 视频播放器标识
const style = computed(() => {
  if (isFullScreen.value) {
    return {
      width: document.documentElement.clientWidth + 'px',
      height: document.documentElement.clientHeight + 'px',
    }
  } else {
    return {
      width: props.width + 'px',
      height: props.height + 'px',
    }
  }
})
// 创建视频
function createVideo() {
  videoRef.value = document.createElement('video')
  videoId.value = 'tp-video' + Date.now()
  videoRef.value.id = videoId.value
  videoRef.value.style.display = "none";
  videoRef.value.style['object-fit'] = 'contain';
  videoRef.value.muted = "muted";
  videoRef.value.preload = 'auto'
  if (props.src) {
    videoRef.value.src = props.src
  }
  addVideoListener()
  document.body.append(videoRef.value)
}
// 监听播放进度更改及其他事件
function addVideoListener() {
  // 第一帧加载完成
  videoRef.value.onloadeddata = (_e: any) => {
    videoCanvasCtx.value.drawImage(videoRef.value, 0, 0, props.width, props.height);
    currentTime.value = formatSeconds(Math.floor(videoRef.value.currentTime)) // dayjs.unix(Math.floor(videoRef.value.currentTime)).format('mm:ss')
    duration.value = formatSeconds(Math.floor(videoRef.value.duration)) // dayjs.unix(Math.floor(videoRef.value.duration)).format('mm:ss')
    emit('on-change', { currentTime: currentTime.value, duration: duration.value })
  }
  // 播放时间改变
  videoRef.value.ontimeupdate = (_e: any) => {
    currentTime.value = formatSeconds(Math.floor(videoRef.value.currentTime)) // dayjs.unix(Math.floor(videoRef.value.currentTime)).format('mm:ss')
    progres.value = (videoRef.value.currentTime / videoRef.value.duration) * 100
    progres.value = Number(progres.value.toFixed(2))
    for (let i = 0; i < videoRef.value.buffered.length; i++) {
      if (videoRef.value.buffered.start(videoRef.value.buffered.length - 1 - i) < videoRef.value.currentTime) {
        videoBuffered.value = (videoRef.value.buffered.end(videoRef.value.buffered.length - 1 - i) / videoRef.value.duration) * 100
        break
      }
    }
    emit('on-change', { currentTime: currentTime.value, duration: duration.value, progres: progres.value })
  }
  // 播放结束
  videoRef.value.onended = (_e: any) => { // 播放完成
    isPlay.value = false
    emit('end')
  }
}
// 播放暂停
function handleClick() {
  if (!isPlay.value) {
    videoRef.value.play()
    isPlay.value = true
    renderVideo()
  } else {
    isPlay.value = false
    videoRef.value.pause()
    if (timer.value) clearInterval(timer.value)
  }
}
// 渲染视频
function renderVideo() {
  let canvasWidth = props.width, canvasHeight = props.height
  if (isFullScreen.value) {
    canvasWidth = document.documentElement.clientWidth
    canvasHeight = document.documentElement.clientHeight
  }
  videoCanvas.value.height = canvasHeight
  videoCanvas.value.width = canvasWidth
  // videoCanvasCtx.value.reset()
  if (timer.value) clearInterval(timer.value)
  timer.value = setInterval(() => {
    videoCanvasCtx.value.drawImage(videoRef.value, 0, 0, canvasWidth, canvasHeight);//绘制视频
  }, 30);
}
function handleClickProgres(_e: any) {
  _e.preventDefault()
  const currentProgres = Math.floor((_e.offsetX / progresBoxRef.value.clientWidth) * 100)
  progres.value = currentProgres
  videoRef.value.currentTime = (videoRef.value.duration * currentProgres) / 100
  renderVideo()
}
// 鼠标移入显示控制器
function mouseover() {
  hiddeiAction.value = true
}
// 鼠标移出隐藏控制器
function mouseleave() {
  setTimeout(() => {
    hiddeiAction.value = false
  }, 1000)
}
// 全屏退出全屏
function toggleFullScreen() {
  var video = tpVideoRef.value
  const fullscreenElement = document.fullscreenElement || (document as any).mozFullScreenElement || (document as any).webkitFullscreenElement || (document as any).msFullscreenElement
  if (!fullscreenElement) { // 进入全屏
    try {
      video.requestFullscreen();
      video.mozRequestFullScreen();
      video.webkitRequestFullscreen();
      video.msRequestFullscreen();
    } catch (_e) { }
  } else { // 退出全屏
    try {
      document.exitFullscreen();
      (document as any).mozCancelFullScreen();
      (document as any).webkitExitFullscreen();
      (document as any).msExitFullscreen();
    } catch (_e) { }
  }
  renderVideo()
}
// 监听进入退出全屏
function onFullscreen() {
  if (!document.fullscreenElement) {
    isFullScreen.value = false
  } else {
    isFullScreen.value = true
    videoRef.value.play()
    isPlay.value = true
  }
  renderVideo()
}
onMounted(() => {
  videoCanvasCtx.value = videoCanvas.value.getContext("2d")
  videoCanvasCtx.value.fillStyle = "#000000";
  videoCanvasCtx.value.fillRect(0, 0, props.width, props.height);
  document.addEventListener('fullscreenchange', onFullscreen)
})
onUnmounted(() => {
  const tpVideo = document.body.querySelector('video#' + videoId.value)
  if (tpVideo) tpVideo.remove()
  if (timer.value) clearInterval(timer.value)
  document.removeEventListener('fullscreenchange', onFullscreen)
})
createVideo()
</script>
<style scoped lang="scss">
.tp-video {
  position: relative;
}

.video-canvas {
  position: absolute;
  z-index: 1;
}

.tp-video-box {
  width: 100%;
  height: 20px;
  background: rgba(255, 255, 255, .6);
  position: absolute;
  padding: 5px 10px;
  z-index: 2;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  justify-content: center;
  align-items: center;

  .tp-progres-time {
    font-size: 10px;
    color: #333333;
    margin-left: 5px;
  }

  .fullscreen-icon {
    width: 30px;
    height: 30px;
    background: #ffffff;
    margin-left: 10px;
    cursor: pointer;
  }
}

.tp-progres-box {
  width: 100%;
  height: 6px;
  background: rgb(194, 194, 194);
  border-radius: 10px;
  position: relative;
  cursor: pointer;

  .buffered {
    position: absolute;
    top: 0;
    left: 0;
    height: inherit;
    background: rgb(232, 230, 230);
    border-radius: 10px;
    z-index: 100;
  }

  .tp-progres {
    z-index: 201;
    position: absolute;
    top: 0;
    left: 0;
    height: inherit;
    background: #ffffff;
    border-radius: 10px 0 0 10px;
    .tp-progres-mark {
      position: absolute;
      pointer-events: none;
      top: 0px;
      right: -3px;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: #ffffff;
      transform: scale(1.2);
      &:hover {
        transform: scale(1.2);
      }
    }
  }
}

.tp-video-btn {
  position: absolute;
  z-index: 200;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;

  .icon-play {
    background: #333333;
    width: 22px;
    height: 22px;
  }
}
</style>